package com.dlc.shop.security.common.manager;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.dlc.shop.bean.dto.ThirdApiAbutmentUserDto;
import com.dlc.shop.common.constants.OauthCacheNames;
import com.dlc.shop.common.enums.SysTypeEnum;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.response.ResponseEnum;
import com.dlc.shop.common.util.HttpContextUtils;
import com.dlc.shop.common.util.RedisUtil;
import com.dlc.shop.security.common.bo.LoginInfoBO;
import com.dlc.shop.security.common.bo.UidInfoBO;
import com.dlc.shop.security.common.enums.SocialType;
import com.dlc.shop.security.common.util.AuthUserContext;
import com.dlc.shop.security.common.vo.TokenInfoVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;

/**
 * token管理 1. 登陆返回token 2. 刷新token 3. 清除用户过去token 4. 校验token
 *
 * @author FrozenWatermelon
 * @date 2020/7/2
 */
@Component
public class TokenStore {

    private static final Logger logger = LoggerFactory.getLogger(TokenStore.class);

    private final RedisTemplate<String, Object> redisTemplate;

    private final String tokenName;

    public TokenStore(RedisTemplate<String, Object> redisTemplate,@Value("${sa-token.token-name}") String tokenName) {
        this.redisTemplate = redisTemplate;
        this.tokenName = tokenName;
    }


    /**
     * 获取登录信息
     * @return 用户信息
     */
    public LoginInfoBO getLoginInfoByCache() {
        String accessToken = HttpContextUtils.getHttpServletRequest().getHeader(tokenName);
        // 获取登录的信息
        Object loginInfoCache = RedisUtil.get(OauthCacheNames.LOGIN_INFO + accessToken);
        if (Objects.isNull(loginInfoCache)) {
            return new LoginInfoBO();
        }
        return JSONObject.parseObject(loginInfoCache.toString(), LoginInfoBO.class);
    }


    /**
     * 根据uidInfo信息删除用户token
     * @param uidInfoBO
     */
    public void deleteTokenByUidInfo(UidInfoBO uidInfoBO) {
        if (Objects.isNull(uidInfoBO)) {
            return;
        }
        String uid = AuthUserContext.getUid(uidInfoBO);
        List<String> tokenList = StpUtil.getTokenValueListByLoginId(uid);
        if (CollUtil.isNotEmpty(tokenList)) {
            for (String token : tokenList) {
                // 删除用户缓存
                redisTemplate.delete(OauthCacheNames.LOGIN_INFO + token);
            }
        }
        // 移除token
        StpUtil.logout(uid);
    }


    /**
     * 根据uidList删除多个token
     * @param uidList
     */
    public void deleteAllTokenByUids(List<String> uidList) {
        if (CollUtil.isEmpty(uidList)) {
            return;
        }
        for(String uid : uidList){
            // 移除token
            StpUtil.logout(uid);
        }
    }


    /**
     * 生成token，并返回token展示信息
     * @param uidInfo
     * @return
     */
    public TokenInfoVO storeAndGetVo(UidInfoBO uidInfo) {

        int expiresIn = getExpiresIn(uidInfo.getSysType().value());
        String uid = AuthUserContext.getUid(uidInfo);
        // 通过satoken生成的token
        StpUtil.login(uid, expiresIn);

        // 数据封装返回
        TokenInfoVO tokenInfoVO = new TokenInfoVO();
        tokenInfoVO.setAccessToken(StpUtil.getTokenValue());
        tokenInfoVO.setExpiresIn(expiresIn);
        return tokenInfoVO;
    }


    /**
     * 计算过期时间（单位:秒）
     * @param sysType
     * @return
     */
    public int getExpiresIn(int sysType) {
        // 3600秒(1小时)
        int expiresIn = 3600;
        // 普通用户token过期时间 1小时 * 24 * 30 = 30天
        if (Objects.equals(sysType, SysTypeEnum.ORDINARY.value())) {
            expiresIn = expiresIn * 24 * 30;
        }
        // 系统管理员的token过期时间 1小时 * 24 * 30 = 30天
        if (Objects.equals(sysType, SysTypeEnum.MULTISHOP.value()) || Objects.equals(sysType, SysTypeEnum.PLATFORM.value()) || Objects.equals(sysType, SysTypeEnum.STATION.value())) {
            expiresIn = expiresIn * 24 * 30;
        }
        return expiresIn;
    }


    /**
     * 将第三方token同步到商城
     * @param thirdApiAbutmentUserDto
     */
    public void userTokenSync(ThirdApiAbutmentUserDto thirdApiAbutmentUserDto) {
        UidInfoBO uidInfoBO = new UidInfoBO(SysTypeEnum.ORDINARY, thirdApiAbutmentUserDto.getUserId());
        String uid = AuthUserContext.getUid(uidInfoBO);
        // 将token名称和过期时间设置为第三方的
        SaLoginModel saLoginModel = new SaLoginModel();
        saLoginModel.setToken(thirdApiAbutmentUserDto.getAccessToken());
        saLoginModel.setTimeout(thirdApiAbutmentUserDto.getExpiresIn());
        StpUtil.login(uid, saLoginModel);
        // 小程序或者公众号登录
        if (Objects.equals(thirdApiAbutmentUserDto.getSocialType(), SocialType.MA.value())
                || Objects.equals(thirdApiAbutmentUserDto.getSocialType(), SocialType.MP.value()) ) {
            // 缓存当前登录的openId
            LoginInfoBO loginInfoBO = new LoginInfoBO();
            loginInfoBO.setSocialType(thirdApiAbutmentUserDto.getSocialType());
            loginInfoBO.setBizUserId(thirdApiAbutmentUserDto.getOpenId());
            RedisUtil.set(OauthCacheNames.LOGIN_INFO + thirdApiAbutmentUserDto.getAccessToken(),
                    JSONObject.toJSONString(loginInfoBO),
                    thirdApiAbutmentUserDto.getExpiresIn());
        }
    }

    /**
     * 获取登录用户的uid
     * @return
     */
    public String getUidByToken(String token) {
        Object uid;
        try {
            uid = StpUtil.getLoginIdByToken(token);
        } catch (NotLoginException e) {
            // 登录过期，请重新登录 -- 如果是sa-token未登录异常，需要替换成商城的未登录异常
            throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED, "yami.security.login.expired");
        }
        return uid.toString();
    }


}
